package com.ocm.core.mapping.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.googlecode.simpleobjectassembler.ObjectAssembler;
import com.googlecode.simpleobjectassembler.beans.FallbackPropertyAccessor;
import com.ocm.core.domain.ClassDefinition;
import com.ocm.core.domain.Security;
import com.ocm.core.domain.UserDetail;
import com.ocm.core.domain.Persistable;
import com.ocm.core.domain.PropertyDefinition;
import com.ocm.core.domain.Storage;
import com.ocm.core.domain.UserInfo;
import com.ocm.core.dto.ClassDefinitionDto;
import com.ocm.core.dto.GroupDto;
import com.ocm.core.dto.PropertyDefinitionDto;
import com.ocm.core.dto.SecurityDto;
import com.ocm.core.dto.StorageDto;
import com.ocm.core.dto.UserInfoDto;
import com.ocm.core.exception.OCMRuntimeException;
import com.ocm.core.exception.UnsupportTypeException;
import com.ocm.core.mapping.DomainService;

@Service("domainService")
public class DomainServiceImpl implements DomainService {

   private ObjectAssembler objectAssembler;

   @Autowired
   public DomainServiceImpl(@Qualifier("objectAssembler") ObjectAssembler objectAssembler) {
      this.objectAssembler = objectAssembler;
   }

   public <T extends Persistable> List<T> convertTypeCollection(List<?> objectDtos, Class<T> destinationObject,
         String parentPropertyName, Object parent, String... ignoreProperties) {
      List<T> properties = new ArrayList<T>();
      for (Object sourceObject : objectDtos) {
         T objectT = this.objectAssembler.assemble(sourceObject, destinationObject, ignoreProperties);
         PropertyAccessor destinationPropertyAccessor = new FallbackPropertyAccessor(objectT);
         destinationPropertyAccessor.setPropertyValue(parentPropertyName, parent);
         properties.add(objectT);
      }
      return properties;
   }

   public PropertyDefinition convertProperty(PropertyDefinitionDto propertydto) {
      PropertyDefinition property = this.objectAssembler.assemble(propertydto, PropertyDefinition.class);
      return property;
   }

   public ClassDefinition convertClassDef(ClassDefinitionDto classDefinitionDto) {
      ClassDefinition classDefinition = this.objectAssembler.assemble(classDefinitionDto, ClassDefinition.class, "security","parentClassDefUID");
      Security security = classDefinition.getSecurity();
      Security convertSecurity = this.convertSecurity(classDefinitionDto.getSecurities());
      if (security != null){
         security.setSecurityInformation(convertSecurity.getSecurityInformation());
      }else{
         security = convertSecurity;
      }
      security.setClassDefinition(classDefinition);
      classDefinition.setSecurity(security);
      return classDefinition;
   }

   public Storage convertStorage(StorageDto storageDto) {
      Storage storage = this.objectAssembler.assemble(storageDto, Storage.class);
      return storage;
   }

   public UserDetail convertGroup(GroupDto groupDto) {
      UserDetail group = this.objectAssembler.assemble(groupDto, UserDetail.class);
      return group;
   }

   public UserDetail convertUserInfo(UserInfoDto userInfoDto) {
      UserDetail userDetail = this.objectAssembler.assemble(userInfoDto, UserDetail.class);
      UserInfo userInfo = userDetail.getUserInfo();
      if (userInfo == null){
         userInfo = new UserInfo();
      }
      userInfo.setPassword(userInfoDto.getPassword());
      userInfo.setIsAccountNonExpired(userInfoDto.getIsAccountNonExpired());
      userInfo.setIsAccountNonLocked(userInfoDto.getIsAccountNonLocked());
      userInfo.setUserDetail(userDetail);
      userDetail.setUserInfo(userInfo);
      return userDetail;
   }

   public Security convertSecurity(List<SecurityDto> securities){
      Security security = new Security();
      for (SecurityDto securityDto : securities){
         try {
            security.appendSecurity(securityDto.getUserId(), Security.getSecurity(securityDto));
         } catch (UnsupportTypeException e) {
            throw new OCMRuntimeException(e);
         }
      }
      return security;
   }
}
